package com.mrs.gauss.interfaces.rest;


import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.apache.http.HttpEntity;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Log4j2
public class AuditHiveApi {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static ConcurrentHashMap<String, Integer> tableCounts = new ConcurrentHashMap<>();
    private static LocalDateTime lastEndTime = LocalDateTime.now().minusHours(24); // 初始化时间
    private static List<Map> accessTableList = new ArrayList<>();
    private static String printResponse(CloseableHttpResponse httpResponse) throws Exception {
        HttpEntity entity = httpResponse.getEntity();
        String entiryString = "";
        if (entity != null) {
            entiryString = EntityUtils.toString(entity);

        }
        EntityUtils.consume(entity);
        return entiryString;
    }

    public List<Map> getAccessTableList() throws Exception {
        LocalDateTime currentEndTime = LocalDateTime.now();
        String userFilePath = "./conf/UserInfo.properties";
        InputStream userInfo = null;
        File file = new File(userFilePath);
        if (!file.exists()) {
            log.error("The user info file doesn't exist.");
        }
        userInfo = new BufferedInputStream(new FileInputStream(file));
        ResourceBundle resourceBundle = new PropertyResourceBundle(userInfo);
        String username = resourceBundle.getString("userName");
        String password = resourceBundle.getString("password");
        String ipAddr = resourceBundle.getString("webUrl");
        try {
            String serviceName = "Ranger";
            String url;
            url = ipAddr + "/monitor/service/customize" + serviceName + ".do";
            HttpGet httpGet = new HttpGet(url);
            String authStr = username + ":" + password;
            String encoding = DatatypeConverter.printBase64Binary(authStr.getBytes("UTF-8"));
            httpGet.setHeader("Authorization", "Basic " + encoding);
            CookieStore cookieStore = new BasicCookieStore();
            CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
            CloseableHttpResponse httpResponse = httpClient.execute(httpGet);


            url = ipAddr + "/api/v2/log";
            // 动态生成时间（示例：结束时间=当前时间，开始时间=24小时前）
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

            // 使用上次结束时间作为本次开始时间
            String startTimeStr = lastEndTime.format(formatter);
            String endTimeStr = currentEndTime.format(formatter);
//            System.out.println(startTimeStr+endTimeStr);
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
            String content = "{\"keyWord\":\"\",\"sources\":[{\"sourceType\":\"CLUSTER\",\"clusterId\":1,\"clusterName\":\"\"]" +
                    ",\"logFileName\":\"\",\"minLogLevel\":\"INFO\",\"hosts\":\"\",\"startTime\":\"" + startTimeStr + "\"" +
                    ",\"endTime\":\"" + endTimeStr + "\",\"timeOut\":\"120000\",\"maxLogsNum\":\"1000\"}";
            StringEntity stringEntity = new StringEntity(content);
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);


            httpResponse = httpClient.execute(httpPost);


            url = ipAddr + "/api/v2/log/" + printResponse(httpResponse).split(":")[1]
                    .replace("\"", "").replace("}", "");

            HttpGet httpGet2 = new HttpGet(url);

            CloseableHttpResponse httpResponse2 = null;
            int maxAttempts = 100; // max attempts
            int attempt = 0;
            while (attempt < maxAttempts) {
                try {
                    httpResponse2 = httpClient.execute(httpGet2);
                    if (printResponse(httpResponse2).contains("COMPLETE")) {
                        break;
                    }
                    Thread.sleep(10);
                } catch (Exception e) {
                    System.err.println("ERROR: " + e.getMessage());
                }
                attempt++;
            }
            httpResponse2 = httpClient.execute(httpGet2);
            List<Map> details = jsonToMaps(printResponse(httpResponse2));
            String regex = "tbl=hive.([\\w.]+)(?=\\s|\\|)";
            String no_regex = "ugi=hds";
            Pattern pattern = Pattern.compile(regex);
            StringBuffer stringBuffer = new StringBuffer();
            for (Map detail : details) {
//                System.out.println(String.valueOf(detail.get("details")));
                StringBuffer detailsBuffer = new StringBuffer(String.valueOf(detail.get("details")));
                //排出hds用户执行记录
                if (detailsBuffer.toString().contains(no_regex)) {
                    continue;
                }
                Matcher matcher = pattern.matcher(detailsBuffer.toString());
                if (matcher.find()) {
                    StringBuffer tableName = new StringBuffer(matcher.group(1));
                    //去重
                    if (!tableName.toString().equals(stringBuffer.toString())) {
                        tableCounts.compute(tableName.toString(), (k, v) -> (v == null) ? 1 : v + 1);
                    }
                    stringBuffer.setLength(0);
                    stringBuffer.append(tableName.toString());
                }
            }
            // tableCounts 容量控制
            List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(tableCounts.entrySet());
            sortedEntries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue())); // 降序排序

            int MAX_SIZE = 20000;
            if (sortedEntries.size() > MAX_SIZE) {
                sortedEntries = sortedEntries.subList(0, MAX_SIZE);
                // 重建Map保证线程安全
                ConcurrentHashMap<String, Integer> newTableCounts = new ConcurrentHashMap<>();
                sortedEntries.forEach(entry -> newTableCounts.put(entry.getKey(), entry.getValue()));
                tableCounts = newTableCounts;
            }
            accessTableList = (new ArrayList<>());// 置空操作应传入空列表
            accessTableList = sortAndPrint(tableCounts);
//            System.out.println(accessTableList);
        } catch (Exception e) {
            log.error("定时任务执行异常", e);
        }
        // 更新结束时间记录
        lastEndTime = currentEndTime;
        // 修改返回结果，限制最多50条记录
        return accessTableList.size() > 50 ? accessTableList.subList(0, 50) : accessTableList;
    }


    public static List<Map> jsonToMaps(String json) throws IOException {
        Map map = OBJECT_MAPPER.readValue(json, Map.class);
        return (List) (map.get("details"));
    }

    private static List<Map> sortAndPrint(Map<String, Integer> tableCounts) {
        // 将Map=>List
        List<Map.Entry<String, Integer>> list = new ArrayList<>(tableCounts.entrySet());

        // compareTo,DESC
        list.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
        // 转换为List<Map>
        List<Map> result = new ArrayList<>();
        for (Map.Entry<String, Integer> entry : list) {
            Map<String, Object> map = new HashMap<>();
            map.put("tableName", entry.getKey());
            map.put("count", entry.getValue());
            result.add(map);
        }
        return result;
    }
}
